home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Best of Shareware
/
Best of PC Windows Shareware 1.0 - Wayzata Technology (7111) (1993).iso
/
mac
/
ZIPPED
/
DOS
/
GRAPHICS
/
POVSRC.ZIP
/
MISC.ZIP
/
PORTGUID.TXT
< prev
next >
Wrap
Text File
|
1992-04-17
|
12KB
|
266 lines
Portability Guidelines for POV-Ray by David Buck
-------------------------------------------------
In my experiences with DKBTrace, I've found that there are several principles
which should be followed in order to make the code more portable between
systems. In the interest of making POV-Ray as portable as possible, I've
decided to put some of these principles into writing in order to help others
who are developing code.
This guide is divided into two parts. The first part gives some general
guidelines for making any program more portable from system to system.
The second part describes a specific proposal for making POVRay and other
programs such as GUI's portable.
General Portability Guidelines
------------------------------
Keep in mind that not all compilers are ANSI compliant. Many only support the
original K&R specification.
1) Don't call functions or use variables that are system-specific. This may
only be done in the system-specific C module. This is rather obvious,
but it's worth mentioning.
2) Don't use conditional compiling in the core of the raytracer. The only
places where conditional compilation is acceptable is in frame.h in
order to define defaults for #define'd values, in the config.h file,
and in the system specific C file (and even then, it shouldn't be
necessary.)
3) Be careful about ints. It's usually best to specify short or long.
Not all ints are the same size.
4) On many systems, the toupper and tolower functions only work properly if
the parameters are already lowercase or uppercase respectively. Always
put a test before these calls when converting the case:
if (isupper(c))
c = tolower(c);
5) When declaring external functions or variables in a .h file, always
put the keyword "extern" in front of the definition. Many C compilers
will choke on multiply declared identifiers otherwise.
6) Always use the old pre-ANSI parameter declaration mechanism
i.e.,
int junk (a,b,c)
DBL a;
int b;
int *c;
{...
7) Don't use (void) as a function parameter. If you do this, put PARAMS
around the definition:
int junk PARAMS((void)))
8) Don't take the address of an array using the & operator. Use the name
of the array itself.
eg.
int x[10];
junk(x); is ok, but junk(&x) is not.
9) Avoid using pre-defined words or names of library functions as variable
names. The following are not acceptable as variable names:
max, min, abs, system
10) When declaring a large amount of space, it's best to malloc it instead
of declaring it directly. Some systems have trouble with large objects
allocated staticly.
11) When including header files, don't put in any pathname or subdirectory
name (except possibly in system-specific modules.) Not all systems have
the same characters for pathname separators.
12) When declaring variables to hold values from 0 to 255, use unsigned char.
Don't use "char". This is especially true when representing colors.
Some systems set the top bit of chars to 0 all the time. Using unsigned
chars are ok.
13) When declaring non-static functions (except in system-specific modules),
always add a prototype for the function in the povproto.h file. For system
specific modules, you should include the prototypes at the top of the
system-specific file.
14) When declaring prototypes, always use the PARAMS macro.
15) Don't declare identifiers longer than 32 characters. Many systems break
at that limit. In fact, some compilers break after 8 characters. I
don't intend to compensate for those compilers.
16) Never assume byte ordering. For example, if you want to take a short and
output it as two bytes, use "% 256" and "/ 256". You should be able to
use "&" and ">>", but don't play tricks with pointers.
eg.
Bad:
short x;
putchar (*(unsigned char *)x);
putchar (*((unsigned char *)x+1));
Good:
short x;
putchar (x/256);
putchar (x%256);
Good:
short x;
putchar (x & 0xFF);
putchar ((x >> 8) & 0xFF);
Be especially careful with fwrite's:
fwrite (file, x, 2);
17) Don't assume that all systems can handle vt100 control characters. It's
best to use standard text for output. Don't try to be fancy and change
colors, underlining, etc. on screen output. At the very least, provide
a way of turning it off with a command line option. As it turns out,
some systems (read Mac) don't support standard output or command-line
parameters. Fortunately, we can get around those.
18) If you really need to create temporary files, be sure to delete them
when you are finished. Don't assume that you can just overwrite the
files when the program runs another time. Some systems (Vax) store
several versions of files around and these can take up a lot of space.
As a rule of thumb, it's best to avoid temporary files if at all possible.
They make it difficult for multi-tasking systems to run more than one
session of the program at any one time.
19) Avoid enums. Not all compilers have them and those that do are very
inconsistent about their usage. Many won't allow you to convert enums
to ints and others won't allow you to switch on an enum. Stick with
#define's. It's harder to manage, but it saves on complaints afterwards.
20) If you encounter a major error and must exit the program, be sure to
call close_all first. Some systems have to close windows, screens, etc.
before exiting.
21) All C files must include "config.h". This is required for many systems
that need to override missing system functions with #defines.
22) Never assume you know the size of an int or a pointer. Always use sizeof
to determine the size.
22) When compiling, use as many error checking options as possible. Many
systems warn about unused variables, variables used before set,
missing prototypes, etc. The more of these you can catch yourself, the
better.
23) Avoid closing and re-opening a file frequently. On some systems, this
causes disk head thrashing. You should at least provide a command-line
option to disable this.
24) Don't pass structure parameters. Pass pointers to the structures
instead. Most compilers these days can handle structure copies, but
not many handle structure parameters.
25) When reading or writing binary files, always open them with the "b"
option. Otherwise, many systems will discard '\0' characters and perform
translations on CR's and LF's.
eg.
f = fopen ("filename", "wb");
POV-Ray Portable Graphics Proposal
-----------------------------------
The problem of making a portable graphics program is a difficult one. In many
ways, POV-Ray itself is easy to port because the only real graphics primitive
required is one to draw a point of a certain color at a certain x,y coordinate.
The coordinates are guaranteed to proceed from left to right, top to bottom.
(Incidently, the left-to-right ordering is assumed by the Amiga drivers.)
The problem of making a more general interface for something more complex
such as a GUI is dramatically more difficult. Most systems provide routines
to draw lines, circles, etc. on the screen. Some (eg. Amiga), have special
hardware to do this. On some systems, third party boards have such primitives.
Even the more complex operations are provided in hardware on some systems.
Silicon Graphics Iris workstations, for example, have hardware support for
Gouraud-shaded filled polygons.
Our strategy should follow some common principles:
1) It should be easy to use from an application level. Application programs
shouldn't need to care about the type of system they're running on.
2) It should be able to use any number of colors in your palette from
2 colors to 256. Support for 24 bit color should also be available.
3) People who port the code should be able to provide their own low-level
routines to handle their particular graphics system.
4) If a particular system has special hardware that could speed up certain
operations, the person porting the code should be able to use it easily.
5) Simple ports should be possible by providing only the basic required
functions. The port can be made faster if more primitives are
implemented specifically for that system.
Here's my proposal:
Consider the following system diagram:
-----------------
| |
| Applications |
| |
-----------------
|
|
|
|
------------------- |
| | | ------------------
| System-specific | | | |
| Functions | | | Non-primitives |
| | | | |
------------------- | ------------------
| | |
| | |
| | |
-----------------
| |
| Function |
| Variables |
| |
-----------------
In this diagram, the applications will perform their operations by calling
function variables. This would probably be simplified by providing macros to
perform the actual calls.
When the application starts, it calls two initialization functions:
pov_init_gfx_defaults();
pov_init_gfx_system_specific();
The call to pov_init_gfx_defaults() will call a function in the non-primitives
area to bind default functions to all of the function variables in the base
module. The subsequent call to pov_init_gfx_system_specific() will call a function
in the system-specific area to over-ride functions bound in by the
non-primitive area with functions that work for that specific system.
For example, if we had a draw_line function we could write a non-primitive
function to perform draw_filled_polygon(). This function would be bound to the
appropriate function variable when the non-primitive area is initialized.
If your particular hardware has hardware support for draw_filled_polygon(), then
you can bind in your own function in the system-specific area to perform the
same action.
Sometimes, the system-specific function will want to perform its own work in
addition to the work done by the non-primitive function. In this case, the
system function will store away a copy of the function pointer before it over-
writes it with its own. In its own function, it will call the stored function
pointer in addition to performing its own operations.
The system-specific functions can call other system-specific functions
directly if they desire (although this is not recommended.) The applications
and the non-primitives must make their calls through the function variables.